﻿using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.WindAlert;
using Nop.Services.Events;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Nop.Services.WindAlert
{
    /// <summary>
    /// Spot service
    /// </summary>
    public partial class SpotService : ISpotService
    {
        #region Constants
        private const string SPOTS_ALL_KEY = "Nop.spot.all-{0}-{1}-{2}-{3}-{4}-{5}-{6}";
        private const string SPOTS_BY_ID_KEY = "Nop.spot.id-{0}";
        private const string SPOTS_PATTERN_KEY = "Nop.spot.";

        #endregion

        #region Fields

        private readonly IRepository<Spot> _spotRepository;
        private readonly IRepository<Camera> _cameraRepository;
        private readonly IRepository<SpotComment> _spotCommentRepository;
        private readonly ICacheManager _cacheManager;
        private readonly IEventPublisher _eventPublisher;
        #endregion

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="cacheManager">Cache manager</param>
        /// <param name="spotRepository">Live Spot repository</param>
        public SpotService(ICacheManager cacheManager,
            IRepository<Spot> spotRepository,
            IRepository<Camera> cameraRepository,
            IRepository<SpotComment> spotCommentRepository,
            IEventPublisher eventPublisher)
        {
            this._cacheManager = cacheManager;
            this._spotRepository = spotRepository;
            this._cameraRepository = cameraRepository;
            this._spotCommentRepository = spotCommentRepository;
            _eventPublisher = eventPublisher;
        }
        #endregion

        #region Methods

        /// <summary>
        /// Deletes a spot
        /// </summary>
        /// <param name="spot">Spot</param>
        public virtual void DeleteSpot(Spot spot)
        {
            if (spot == null)
                throw new ArgumentNullException("spot");

            spot.Deleted = true;
            UpdateSpot(spot);
        }


        /// <summary>
        /// Gets all spots
        /// </summary>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <param name="spotId">Spot Id</param>
        /// <returns>Spot collection</returns>
        public virtual IList<Spot> GetAllSpots(bool showHidden = false, bool isForecast = false, bool isWave = false,
            bool isStation = false, bool isMetar = false, bool showOnHome = false, bool showOnAviasion = false)
        {
            string key = string.Format(SPOTS_ALL_KEY, showHidden, isForecast, isWave,isStation,isMetar,showOnHome,showOnAviasion);
            return _cacheManager.Get(key, () =>
            {
                var query = from m in _spotRepository.Table
                            orderby m.DisplayOrder
                            where (showHidden || m.Published) &&
                            !m.Deleted 
                            select m;
                if (isStation)
                    query = query.Where(s => s.IsStation);
                if (isMetar)
                    query = query.Where(s => s.StationIsMetar);  
                if (isForecast)
                    query = query.Where(s=> s.IsForecast);
                if (isWave)
                    query = query.Where(s => s.IsWave);
                if (showOnHome)
                    query = query.Where(s => s.ShowOnHome);
                if (showOnAviasion)
                    query = query.Where(s => s.ShowOnAviasion);
                var spots = query.ToList();
                return spots;
            });
        }

        /// <summary>
        /// Gets all spots
        /// </summary>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <param name="spotId">Spot Id</param>
        /// <returns>Spots</returns>
        public virtual IPagedList<Spot> GetAllSpots(int pageIndex, int pageSize,
                                            bool showHidden = false, bool isForecast = false, bool isWave = false,
            bool isStation = false, bool isMetar = false, bool showOnHome = false, bool showOnAviasion = false)
        {
            var spots = GetAllSpots(showHidden,isForecast, isWave, isStation,showOnHome,showOnAviasion);
            return new PagedList<Spot>(spots, pageIndex, pageSize);
        }

        /// <summary>
        /// Gets a spot
        /// </summary>
        /// <param name="spotId">Spot identifier</param>
        /// <returns>Spot</returns>
        public virtual Spot GetSpotById(int spotId)
        {
            if (spotId == 0)
                return null;

            string key = string.Format(SPOTS_BY_ID_KEY, spotId);
            return _cacheManager.Get(key, () =>
            {
                var spot = _spotRepository.GetById(spotId);
                return spot;
            });
        }

        /// <summary>
        /// Inserts a spot
        /// </summary>
        /// <param name="spot">Spot</param>
        public virtual void InsertSpot(Spot spot)
        {
            if (spot == null)
                throw new ArgumentNullException("spot");

            _spotRepository.Insert(spot);

            //cache
            _cacheManager.RemoveByPattern(SPOTS_PATTERN_KEY);
        }

        /// <summary>
        /// Updates the spot
        /// </summary>
        /// <param name="spot">Spot</param>
        public virtual void UpdateSpot(Spot spot)
        {
            if (spot == null)
                throw new ArgumentNullException("spot");

            _spotRepository.Update(spot);

            //cache
            _cacheManager.RemoveByPattern(SPOTS_PATTERN_KEY);
        }

        #region Comments

        /// <summary>
        /// Gets all comments
        /// </summary>
        /// <param name="dateFrom">Filter by created date; null if you want to get all records</param>
        /// <param name="dateTo">Filter by created date; null if you want to get all records</param>
        /// <param name="customerId">Customer identifier; 0 to load all records</param>
        /// <returns>Comments</returns>
        public virtual IList<SpotComment> GetAllComments(int spotId,int customerId, DateTime? dateFrom, DateTime? dateTo)
        {
            var query = from c in _spotCommentRepository.Table
                        orderby c.CreatedOnUtc
                        where (customerId == 0 || c.CustomerId == customerId)
                        select c;
            if (spotId > 0)
                query = query.Where(c => c.SpotId == spotId);
            if (dateFrom.HasValue)
                query = query.Where(c => dateFrom.Value <= c.CreatedOnUtc);
            if (dateTo.HasValue)
                query = query.Where(c => dateTo.Value >= c.CreatedOnUtc);
            var content = query.ToList();

            return content;
        }

        /// <summary>
        /// Gets a spot comment
        /// </summary>
        /// <param name="spotCommentId">Spot comment identifier</param>
        /// <returns>Spot comment</returns>
        public virtual SpotComment GetSpotCommentById(int spotCommentId)
        {
            if (spotCommentId == 0)
                return null;

            return _spotCommentRepository.GetById(spotCommentId);
        }

        /// <summary>
        /// Deletes a spot comment
        /// </summary>
        /// <param name="spotComment">Spot comment</param>
        public virtual void DeleteSpotComment(SpotComment spotComment)
        {
            if (spotComment == null)
                throw new ArgumentNullException("spotComment");

            _spotCommentRepository.Delete(spotComment);
        }

        /// <summary>
        /// Check whether customer is allowed to edit comment
        /// </summary>
        /// <param name="customer">Customer</param>
        /// <param name="comment">Topic</param>
        /// <returns>True if allowed, otherwise false</returns>
        public virtual bool IsCustomerAllowedToEditDeleteComment(Customer customer, SpotComment comment)
        {
            if (comment == null)
            {
                return false;
            }

            if (customer == null)
            {
                return false;
            }

            if (customer.IsGuest())
            {
                return false;
            }

            if (customer.IsAdmin())
            {
                return true;
            }

            bool ownComment = customer.Id == comment.CustomerId;
            return ownComment;
        }


        #endregion

        #endregion

    }
}
